(function () {
  let inWorker = false;
  try {
    document;
  } catch (_) {
    inWorker = true;
  }

  const getId = function getId() {
    return (+new Date()).toString(32);
  };

  if (inWorker) {
    if (!self.SubWorker) {
      const workers = {};

      self.onmessage = function onmessage(ev) {
        const data = ev.data;
        if (!data._subWorker) {
          self.onMessage(ev);
          return;
        }
        workers[data.id].onmessage(new MessageEvent('worker', {
          data: data.data,
        }));
      };

      class SubWorker {
        constructor (f) {
          this.id = getId();
          workers[this.id] = this;
          self.postMessage({
            _subWorker: true,
            type: 'create',
            id: this.id,
            file: f,
          });
        }
        postMessage(data) {
          self.postMessage({
            _subWorker: true,
            type: 'msg',
            id: this.id,
            data: data,
          });
        }
        terminate() {
          self.postMessage({
            _subWorker: true,
            type: 'terminate',
            id: this.id,
          });
        }
      }

      self.SubWorker = SubWorker;
    }
  } else {
    const workers = {};

    class SubWorker {
      constructor (f, id, parentId) {
        this.id = id || getId();
        this.worker = new Worker(f);
        this.worker.onmessage = this.handleMessage.bind(this);
        this.parentId = parentId;
        workers[this.id] = this;
      }
      handleMessage(ev) {
        const data = ev.data;
        if (!data._subWorker) {
          if (this.parentId) {
            workers[this.parentId].postMessage({
              _subWorker: true,
              type: 'msg',
              id: this.id,
              data: data,
            });
          } else {
            this.onmessage(ev);
          }
          return;
        }
        if (data.type === 'create') {
          const subWorker = new SubWorker(data.file, data.id, this.id);
        } else if (data.type === 'msg') {
          workers[data.id].postMessage(data.data);
        } else if (data.type === 'terminate') {
          workers[data.id].terminate();
        }
      }
      postMessage(data) {
        this.worker.postMessage(data);
      }
      terminate() {
        this.worker.terminate();
      }
    }

    self.SubWorker = SubWorker;
  }
})();
